package com.gxa.learning.portal.controller;


import com.gxa.learning.core.common.ExceptionCode;
import com.gxa.learning.core.common.JsonResult;
import com.gxa.learning.core.exception.BusinessException;
import lombok.extern.log4j.Log4j2;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

/**
 * ControllerAdvice本质上就是一个aop的通知,专门用于加强异常出现的时候的抛出通知
 * Log4j2在使用log类中,直接注解声明
 *
 * @author YXQ
 * @date 2021/10/21 20:32
 */
@RestController
@ControllerAdvice
@Log4j2
public class ExceptionController {
    /**
     * 处理第一种校验异常: 将异常信息拼接,返回给前端显示
     *
     * @param errors 异常
     * @return 返回给前端
     */
    @ExceptionHandler({BindException.class, MethodArgumentNotValidException.class})
    public Map<String, Object> handleBindException(Errors errors) {
        // 声明jsonResult给前端返回
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("0");
        StringBuilder stringBuilder = new StringBuilder();
        errors.getAllErrors().forEach(error -> {
            stringBuilder.append(error.getDefaultMessage()).append(",");
        });
        jsonResult.setMsg(stringBuilder.toString());
        // 返回给前端
        return jsonResult.getDataMap();
    }

    // 处理第二种校验异常: 将异常信息拼接,返回给前端显示
    @ExceptionHandler(ConstraintViolationException.class)
    public Map<String, Object> handleConstraintViolationException(ConstraintViolationException exception) {
        StringBuilder errorInfo = new StringBuilder();
        String errorMessage;

        Set<ConstraintViolation<?>> violations = exception.getConstraintViolations();
        for (ConstraintViolation<?> item : violations) {
            errorInfo.append(item.getMessage()).append(",");
        }
        errorMessage = errorInfo.toString().substring(0, errorInfo.toString().length() - 1);
        JsonResult jsonResult = new JsonResult();
        jsonResult.setCode("0");
        jsonResult.setMsg(errorMessage);
        // 返回给前端
        return jsonResult.getDataMap();
    }

    /**
     * 集中处理业务异常
     *
     * @param e 异常
     * @return 给前端返回
     */
    @ExceptionHandler(value = {BusinessException.class})
    public Map<String, Object> handlerBusinessException(Exception e) {
        // 暂时打印在控制台--打包时删掉它
        e.printStackTrace();
        // 声明 JsonResult 给前端返回
        JsonResult jsonResult = new JsonResult();
        // 从异常信息中获取枚举对象
        ExceptionCode exceptionCode = ((BusinessException) e).getExceptionCode();
        // 从枚举对象中提取错误码给JsonResult
        jsonResult.setCode("" + exceptionCode.getCode());
        // 从枚举对象中提取错误信息给JsonResult
        jsonResult.setMsg(exceptionCode.getMessage());
        // 返回给前端
        return jsonResult.getDataMap();
    }


    /**
     * 当有异常发生,抛出通知介入到目标操作
     * 并把异常对象注入到参数Exception中
     * value表示什么类型的错误或异常才会被记录
     * 我们选择使用Throwable.class(Error和Exception都记录)
     */
    @ExceptionHandler(value = {Throwable.class})
    public Map<String, Object> handler(Exception e, HttpServletRequest request) throws IOException {
        // 系统异常记录到日志文件中(通过log4j2)
        // 声明一个可变字符串
        StringBuilder stringBuilder = new StringBuilder();
        // 开始追加
        stringBuilder.append("----------------begin----------------");
        stringBuilder.append(e);
        // 再遍历栈中的异常轨迹
        StackTraceElement[] stackTrace = e.getStackTrace();
        // 遍历异常轨迹
        for (StackTraceElement stackTraceElement : stackTrace) {
            // 写入日志文件
            stringBuilder.append(stackTraceElement);
        }
        stringBuilder.append("----------------end----------------");
        // 计入异常日志
        log.info(stringBuilder);
        // 声明 JsonResult 给前端返回
        JsonResult jsonResult = new JsonResult();
        // 给前端的反馈
        // 状态码:1002
        jsonResult.setCode("" + ExceptionCode.SERVICE_ERROR.getCode());
        // 信息:服务器开小差了
        jsonResult.setMsg(ExceptionCode.SERVICE_ERROR.getMessage());
        // 给前端反馈
        return jsonResult.getDataMap();
    }
}
